package com.zhouheng.springbootmultithreading.tuling.jmm.success;

/**
 * 通过添加volatile解决
 * 1.flag上加volatile可以
 * 2.count上加volatile可以
 * 3.将count的类型从int改为Integer也行  因为Integer内部value值为 final int value;修饰，final JVM优化的，但是源码没找到
 *
 * x86系统下 volatile原理：内存屏障
 * storeLoad  JVM内存屏障  ---->  (汇编层面指令)  lock; addl $0,0(%%rsp)
 * lock前缀指令不是内存屏障的指令，但是有内存屏障的效果   立即将缓存写回主内存，并让其他线程本地缓存副本失效
 * 本地缓存失效，就只有立即从主内存中拉，即保证了可见性
 *
 * CPU缓存一致性协议——MESI
 * M：修改
 * E：独享
 * S：共享
 * I：失效
 * 如果A,B线程同时读取值，且A计算完还没会回写时，
 * 1.B也计算完了，A此时回写，但B因为是失效状态不会回写，最终结果就是A计算的结果
 * 2.B还没开始计算，A此时回写，此时B发现变量已经失效了，会重新去内存中去拿变量值来开始计算，最终结果就是A和B都计算的正确的结果
 *
 * 计算是把缓存拿到寄存器中，通过计算单元开始计算了
 *
 * 想查看JVM指令编译到汇编指令
 * 1.VM需要加执行参数-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp
 * 2.并下载hsdis-amd64.dll放到jdk的包里
 * 就可以看到最终汇编的lock; addl $0,0(%%rsp)指令
 *
 */
public class VisibilitySuccessTest1 {

    private volatile boolean flag = true;
    private Integer count = 0;

    public void refresh() {
        flag = false;
        System.out.println(Thread.currentThread().getName() + "修改flag:" + flag);
    }

    public void load() {
        System.out.println(Thread.currentThread().getName() + "开始执行.....");
        while (flag) {
            //TODO  业务逻辑
            count++;
        }
        System.out.println(Thread.currentThread().getName() + "跳出循环: count=" + count);
    }

    public static void main(String[] args) throws InterruptedException {
        VisibilitySuccessTest1 test = new VisibilitySuccessTest1();

        // 线程threadA模拟数据加载场景
        Thread threadA = new Thread(() -> test.load(), "threadA");
        threadA.start();

        // 让threadA执行一会儿
        Thread.sleep(1000);
        // 线程threadB通过flag控制threadA的执行时间
        Thread threadB = new Thread(() -> test.refresh(), "threadB");
        threadB.start();
    }

}
